00.Groovy
Groovy 语法
Groovy 基本语法
- 在 Groovy 中导入语句
用import语句来导入
import groovy.xml.MarkupBuilder
def xml = new MarkupBuilder()
默认情况下,Groovy 在代码中导入了以下库,这些库不需要显示地导入它们:
import java.lang.*
import java.util.*
import java.io.*
import java.net.*
import groovy.lang.*
import groovy.util.*
import java.math.BigInteger
import java.math.BigDecimal
- Groovy 令牌 (Token)
令牌可以是一个关键字、一个标识符、常量、字符串文字或符号
println("Hello World")
上面的代码中有,有两个令牌:关键字 println 和字符串 Hello World。
- Groovy 注释
单行注释//
多行注释/* */
文档注释/** */ - 分号
像 Java 语言一样,需要分号在 Groovy 定义的多个语句之间进行区分。 - 标识符 (Identifiers)
标识符被用来定义变量、函数或其他用户定义的变量。
def employeeName
def student
def stu_name
- 关键字 (Keywords)
关键字作为名称建议是在 Groovy 编程语言中保留的特殊字。 下表列出了在 Groovy 中定义的关键字。

- 空白 (Whitespaces)
空白是在编程语言如 Java 和 Groovy 用来形容空格,制表符,换行符和注释术语。空格分隔从另一个声明的一部分,使编译器,其中一个元素标识的声明。
例如,在下面的代码示例,存在关键字 def 和变量 x 之间的空白。这是为了让编译器知道 DEF 是需要被使用,并且是 x 应该是需要被定义的变量名的关键字。
def x = 5;
- 文字 (Literals)
文字是在 groovy 中表示固定值的符号。Groovy 语言有符号整数,浮点数,字符和字符串。下面是一些在 Groovy 编程语言文字的例子:
12
1.45
‘a’
“aa”
- Groovy 变量
Groovy 中的变量可以通过两种方式定义:使用数据类型的本地语法,或者使用 def 关键字。
// 明确提供类型
int x = 5;
用def关键字
def _Name = "Joe";
//强类型定义
//int x=1
//double y=3.14
//char ch='a'
//boolean flag=true;
//弱类型定义
def x=1 //如果没有初始值,是不能直接使用的,会报NullPointerException
def y=3.14
def ch='a'
def flag=true
//println x.class
//println y.class
//println ch.class
//println flag.class
//弱类型是可以自动转型的
x='jett'
y="jett"
ch='''jett'''
println x.class
println y.class
println ch.class
Groovy 运算符
运算符是一个符号,通知编译器执行特定的数学或逻辑操作。
Groovy 中有以下类型的运算符:
- 算法运算符
- 关系运算符
- 逻辑运算符
- 位运算符
- 赋值运算符
算法运算符
Groovy 语言支持正常的算术运算符任何语言。以下是在 Groovy 中可用的算术运算符:
| 运算符 | 描述 | 例子 |
|---|---|---|
| + | 两个操作数的加法 | 1 + 2 将得到 3 |
| - | 第一第二操作数相减 | 2 - 1 将得到 1 |
| * | 两个操作数的乘法 | 2 * 2 将得到 4 |
| / | 两个操作数的除法 | 3/2 将得到 1.5 |
| % | 取模运算 | 3%2 将得到 1 |
| ++ | 自增运算,在自身值的基础上加 1 | INT X = 5;X ++;X 将得到 6 |
| -- | 自减运算,在自身值的基础上减 1 | INT X = 5;X - -;X 将得到 4 |
关系运算符
关系运算符允许对象的比较。以下是在 Groovy 中可用的关系运算符:
- ==
测试两个对象之间是否相等 - !=
测试两个对象之间是否不等 - <
检查是否左边的对象是小于右边的对象 - <=
检查是否向左对象是小于或等于右边的对象
检查是否左边的对象比右边的对象大
-
=
检查是否向左对象大于或等于右边的对象
逻辑运算符
逻辑运算符用于计算布尔表达式:
- && 逻辑 " 与 " 运算
- || 逻辑 " 或 " 运算
- ! 逻辑 " 非 " 运算
位运算符
Groovy 中提供了四个位运算符。以下是在 Groovy 中可用的位运算符:
- &
按位 " 与 " 运算 - |
按位 " 或 " 运算 - ^
按位 " 异或 " 运算 - ~
按位 " 反 " 运算
赋值运算符
Groovy 语言也提供了赋值操作符。以下是在 Groovy 提供的赋值运算符:
- +=
- -=
- *=
- /=
- (%)=
范围运算符
Groovy 支持范围的概念,并在..符号的帮助下提供范围运算符的符号:
def range = 0..5
这只是定义了一个简单的整数范围,存储到一个局部变量称为范围内的下限为 0 和上限为 5。
运算符优先级
Groovy 数据类型
内置数据类型
byte 用来表示字节值
short 用来表示一个短整型
int 用来表示整数
long 用来表示一个长整型
float 用来表示32位浮点数
double 用来表示64位浮点数
char 定义了单个字符文字
Boolean 表示一个布尔值,可以是true和false
String 以字符串的形式表示的文本
数字类
类型除了基本类型,还允许以下对象类型(有时称为包装器类型)
java.lang.Byte
java.lang.Short
java.lang.Integer
java.lang.Long
java.lang.Float
java.lang.Double
此外,以下类可用于支持高精度计算:
java.math.BigInteger 不可变的任意精度的有符号整数数字
Java.math.BigDecimal 不可变的任意精度的有符号十进制数
Groovy 循环
循环 while/for/for-in
- while
while 语句首先通过计算条件表达式(布尔值)来执行,如果结果为真,则执行 while 循环中的语句。
int count = 0
while (count < 5) {
println(count)
count++
}
- for
for 语句用于遍历一组值。
for (int i = 0; i < 5; i++) {
print(" " + i)
}
- for in
for-in 语句用于遍历一组值。
int[] arr = [1, 2, 3, 4]
for (int i in arr) {
print(i + " ")
}
循环控制语句 break/continue
- break
break 语句用于改变循环和 switch 语句内的控制流。 - continue
continue 语句补充了 break 语句。它的使用仅限于 while 和 for 循环。
Groovy 条件语句
条件声明需要程序指定一个或者多个条件进行判断,如果条件被确定为真,则要执行一个或多个语句;如果条件被确定为假,则要执行其他语句。
- if 语句
这个语句的一般工作是首先在 if 语句中计算一个条件。如果条件为真,它然后执行语句。 - if/else 语句
这个语句的一般工作是首先在 if 语句中计算一个条件。如果条件为真,则其后执行语句,并在 else 条件之前停止并退出循环。如果条件为假,则执行 else 语句块中的语句,然后退出循环。 - 嵌套 if 语句
int a = 97
if (a > 100) {
println("大于100")
} else if (a > 90) {
println("大于90")
} else {
println("小于90")
}
- switch 语句
同 Java 中的 switch 语句 - 嵌套 switch 语句
switch 中嵌套 switch 语句
Groovy 方法
Groovy 中的方法是使用返回类型或用 def 关键字定义的。方法可以接收任意的参数。定义参数时,不必显示定义类型。可以添加修饰符(public,private,protected)。默认情况下,如果没提供修饰符,默认为 public。
方法参数
static def sum(int a, int b) {
return a + b
}
默认参数
Groovy 中还有一个规定来指定方法中的参数的默认值。 如果没有值传递给参数的方法,则使用缺省值。 如果使用非默认和默认参数,则必须注意,默认参数应在参数列表的末尾定义。
def sum1(int a, int b = 5) {
return a + b
}
方法返回值
方法也可以将值返回到调用程序。 这在现在编程语言中是必需的,其中方法执行某种计算,然后将所需值返回到调用方法。
其他同 Java 语言特性
实例方法 (setter/getter)、本地和全局参数、方法属性 (this)、
Groovy 文件 I/O
Groovy 在使用 I/O 时提供了许多辅助方法。 Groovy 提供了更简单的类来为文件提供以下功能:
- 读取文件
- 写入文件
- 遍历文件树
- 读取和写入数据对象到文件
始终可以使用下面列出的用于文件 I / O 操作的标准 Java 类:
- java.io.File
- java.io.InputStream
- java.io.OutputStream
- java.io.Reader
- java.io.Writer
1、读取文件
以下示例将输出 Groovy 中的文本文件的所有行。方法 eachLine 内置在 Groovy 中的 File 类中,目的是确保文本文件的每一行都被读取。
static void test1() {
def name = "C:/Users/zengfansheng/Desktop/example.txt"
File file = new File(name)
file.eachLine {
line -> println("line:$line")
}
}
File 类用于实例化以文件名作为参数的新对象。 然后它接受 eachLine 的函数,将它放到一个 line 的变量并相应地打印它。
2、读取文件的内容到字符串
如果要将文件的整个内容作为字符串获取,可以使用文件类的 text 属性:
File file = new File("C:/Users/zengfansheng/Desktop/example.txt")
println file.text
3、写入文件
如果你想写入文件,你需要使用作家类输出文本到一个文件中。
File file = new File("C:/Users/zengfansheng/Desktop/", "example2.txt")
file.withWriter("utf-8") {
writer -> writer.writeLine "hello world write file."
}
4、获取文件的大小
如果要获取文件的大小,可以使用文件类的 length 属性来获取文件的大小。
File file = new File("C:/Users/zengfansheng/Desktop/", "example2.txt")
println("The file ${file.absolutePath} has ${file.length()} bytes")
5、测试文件是否是目录
File file = new File("C:/Users/zengfansheng/Desktop/", "example2.txt")
println "File? ${file.isFile()}"
println "Directory? ${file.isDirectory()}"
6、创建目录
如果要创建一个新目录,可以使用 File 类的 mkdir 函数。
File file = new File("C:/Users/zengfansheng/Desktop/test11")
file.mkdirs() // file.mkdir()
7、删除文件
file.delete()
8、复制文件
File src = new File("C:/Users/zengfansheng/Desktop/", "src.txt")
File dst = new File("C:/Users/zengfansheng/Desktop/", "dst.txt")
dst << src.text
将创建文件 dst.txt,并将文件 src.txt 的所有内容复制到此文件。
9、获取目录内容
Groovy 还提供了列出驱动器中的驱动器和文件的功能。
以下示例显示如何使用 File 类的 listRoots() 函数显示机器上的所有驱动器:
def rootFiles = new File("C:/Users/zengfansheng/Desktop/hexo")
File[] files = rootFiles.listRoots()
files.each {
file -> println file.absolutePath
}
以下示例显示如何使用 File 类的 eachFile() 函数列出特定目录中的文件(仅列出目录的子文目录和文件):
def rootFiles = new File("C:/Users/zengfansheng/Desktop/andfix")
rootFiles.eachFile {
file -> println file.absolutePath
}
如果要递归显示目录及其子目录中的所有文件,则可以使用 File 类的 eachFileRecurse() 函数。以下示例显示如何完成此操作。
def rootFiles = new File("C:/Users/zengfansheng/Desktop/andfix")
rootFiles.eachFileRecurse {
file -> println file.absolutePath
}
Groovy 字符串
Groovy 提供了多种表示 String 字面量的方法。 Groovy 中的字符串可以用 单引号('),双引号(") 或 三引号("') 括起来。此外,由三重引号括起来的 Groovy 字符串可以跨越多行。
String a = 'this is a string'
String b = "this is b string"
String c = "' fajlfdjdlkasjfdljc" +
"fjaldjf '"
字符串索引
Groovy 中的字符串是字符的有序序列。字符串中的单个字符可以通过其位置访问。这由索引位置给出。
字符串索引从零开始,以小于字符串长度的一个结束。 Groovy 还允许负索引从字符串的末尾开始计数。
String a = 'this is a string'
println(a[3]) // s 3表示第4个字符
println(a[-2]) // n , -2表示倒数第2个字符
字符串操作
1、+ 字符串连接
字符串连接可以通过简单的 + 运算符来完成。
String a = "Hello"
String b = "World"
println(a+b)
2、* 字符串重复
字符串的重复可以通过简单的 * 运算符完成。
String*number
- 参数:
- 一个字符串作为 * 操作符的左操作数
- 操作符右侧的数字,表示字符串需要重复的次数。
- 案例:
String a = "Hello"
println(a*3)
3、length() 字符串的长度
字符串方法
除了 Java 中 String 的方法外,还有:
- String center(Number numberOfChars)
返回一个新的长度为 numberOfChars 的字符串,如果 numberOfChars 小于 String 本身长度,那么返回 String 本身;如果 numberOfChars 大于 String 本身长度,该字符串由左侧和右侧用空格字符达到 numberOfChars 长度字符串 - int compareToIgnoreCase(String str)
按字母顺序比较两个字符串,忽略大小写差异。返回一个负整数,零或正整数,因为指定的 String 大于,等于或小于此 String,忽略大小写注意事项。 - void eachMatch(String regex, Closure close)
处理每个正则表达式组匹配的给定 String 的子字符串
String hello = "Hello World!"
hello.eachMatch(".") {
ch -> println(ch)
}
- String minus(Object) & String plus(Object)
minus() 返回删除字符串的值部分新的字符串。
plus() 返回原字符串和追加字符串组成的新的字符串
String hello = "Hello World!"
println(hello.minus("Hell")) // o World!
println(hello.plus("hacket")) // Hello World!hacket
- String next() & String previous()
next() 此方法由 ++ 运算符为 String 类调用。它增加给定字符串中的最后一个字符。
previous() 和 next() 相反,最后一个字符 -- 操作
String hello = "Hello World"
println(hello.next()) // Hello Worle
println(hello.previous()) // Hello Worlc
- String padLeft(Number numberOfCharacters,String padding) & String padRight(Number numberOfCharacters,String padding)
padLeft() numberOfCharacters 小于 String 本身,返回 String 本身;如果大于 String 本身,在左边追加 numberOfCharacters-length() 个 padding
padRight() numberOfCharacters 小于 String 本身,返回 String 本身;如果大于 String 本身,在右边追加 numberOfCharacters-length() 个 padding
String hello = "Hello World"
println(hello.padLeft(hello.length() + 4, "+++")) // ++++Hello World
println(hello.padRight(hello.length() + 1, "-")) // Hello World-
- String reverse()
创建一个与此 String 相反的新字符串。
String hello = "Hello World"
println(hello.reverse()) // dlroW olleH
//单引号 和java中是一样的
def name='jett'
//双引号
def name2="Hello:${name}"
//三引号 原始格式
def name3='''hello
jett "'''
//println name
//println name2
//println name3
//println name.class
//println name2.class
//println name3.class
//输入表达式
//def sum="${3+2}${name}"
//println sum
//
//String echo(String msg){
// println msg
//}
//echo(sum)
def string='hello'
def string2='el'
//groovy中常用的string相关的API
println string>string2
println string[1..2]
//减法
println string.minus(string2)
//逆序
println string.reverse()
//首字母大写
println string.capitalize()
//字符串中是否有数字字符
println string.isEmpty()
Groovy 范围 (ranges)
Range 是指定范围值的序列。Range 由序列中的第一个和最后一个值表示,Range 可以是 inclusive 或 exclusive。inclusive Range 包括从第一个到最后一个的所有值,exclusive Range 包括除最后一个之外的所有值。
- 1 .. 10 - 包含范围的示例
- 1 ..< 10 - 排除范围的示例
- 'a' .. 'x' - 范围也可以由字符组成
- 10 .. 1 - 范围也可以按降序排列
- 'x' .. 'a' - 范围也可以由字符组成并按降序排列。
Range 方法
- boolean contains(Object obj)
检查范围是否包含 obj。如果此范围包含指定的元素,则返回 true。 - Object get(int index)
返回此范围中指定位置 index 处的元素。 - Comparable getFrom()
获取此范围的较低值。不管是升序还是降序 - Comparable getTo()
获取此范围的上限值。不管是升序还是降序 - boolean isReverse()
这是一个反向的范围。是否范围反转的布尔值 true 或 false。(升序为 false,降序为 true) - int size()
返回此范围中的元素数。 - List subList(int fromIndex, int toIndex)
返回此指定的 fromIndex(包括)和 toIndex(排除)之间的此范围部分的视图。
static void test10() {
def v1 = 1..10
def v2 = 1..<10
def v3 = 'a'..'x'
def v4 = 10..1
def v5 = 'x'..'a'
println(v1)
println(v2)
println(v3)
println(v4)
println(v5)
println("================================")
println(v1.contains(8))
println(v1.contains(-2))
println("================================")
println(v2.get(0))
// println(v2.get(11))
println("================================")
println(v1.getFrom())
println(v4.getFrom())
println(v1.getTo())
println(v4.getTo())
println("================================")
println(v1.isReverse())
println(v4.isReverse())
println("================================")
println(v1.size())
println(v2.size())
println("================================")
println(v1.subList(1, 4))
}
运行结果:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x]
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
[x, w, v, u, t, s, r, q, p, o, n, m, l, k, j, i, h, g, f, e, d, c, b, a]
================================
true
false
================================
1
================================
1
1
10
10
================================
false
true
================================
10
9
================================
[2, 3, 4]
Groovy 列表 (Lists)
列表是用于存储数据项集合的结构。在 Groovy 中,List 保存了一系列对象引用。List 中的对象引用占据序列中的位置,并通过整数索引来区分。列表文字表示为一系列用逗号分隔并用方括号括起来的对象。
要处理列表中的数据,我们必须能够访问各个元素。 Groovy 列表使用索引操作符 [] 索引。列表索引从零开始,这指的是第一个元素。以下是一些列表的示例:
[11,12,13,14]- 整数值列表['Angular','Groovy','Java']- 字符串列表[1,2,[3,4],5]- 嵌套列表['Groovy',21,2.11]- 异构的对象引用列表[]- 一个空列表
List 中的方法
- boolean add(Object value) 将新值附加到此列表的末尾
- void add(int index,Object value) 将新值附加到列表中的特定位置 index
- boolean contains(Object value) 如果此列表包含指定的值 value,则返回 true
- Object get(int index) 返回此列表中指定位置 index 处的元素
除了通过 get(index) 方法,也可以通过[index]来访问 - boolean isEmpty() 如果此列表不包含元素,则返回 true。
- List minus(Collection collection) 创建一个由原始元素组成的新列表,而不是集合中指定的元素。(原 List 元素不变)
- List plus(Collection collection) 创建由原始元素和集合中指定的元素组成的新列表。(原 List 元素不变)
- Object pop() 从此列表中删除最后一个项目,如果没有值了会抛异常
- Object remove(int index) 删除此列表中指定位置 index 的元素。
- List reverse() 创建与原始列表的元素相反的新列表。
- int size() 获取此列表中的元素数。
- List sort() 返回原始列表的排序副本。
////定义list
////def list=new ArrayList()
//def list=[1,2,3,4,5]
//println list.class
//println list.size()
////定义数组
//def array=[1,2,3,4,5] as int[]
//
////1.添加
//list.add(6)
//list<<2
//println list
//def plusList=list+5
////println plusList
//plusList.add(3,9)
////println plusList
//2.删除
//list.remove(2) //删除下标位置的对象
//list.remove((Object)2)
//list.removeElement(2)
//list.removeAll{
// return it%2!=0
//}
//println list-[2,4]
////3.查找
//def findList=[5,-2,1,4,3]
////查找满足条件的第一个数据
//int result=findList.find{
// return it%2 == 0
//}
//println result
////查找所有满足条件的数据
//def result2=findList.findAll({
// return it%2 !=0
//})
//println result2
//
////查找是否有满足条件的数据
//def result3=findList.any{
// return it%2 ==0
//}
//println result3
//
////查找是否全部满足条件
//def result4=findList.every{
// return it%2 ==0
//}
//println result4
//
////查找最大值与最小值
//def result5=findList.min{
// return Math.abs(it)
//}
//println result5
//def result6=findList.max{
// return Math.abs(it)
//}
//println result6
////统计
//int result7=findList.count{
// return it>0
//}
//println result7
//4.排序
def sortList=[5,-2,1,4,3]
sortList.sort({a,b ->
a == b ? 0 : Math.abs(a)>Math.abs(b) ? 1 :-1
})
println sortList
//对象排序
def sortStringList=['aaaaa','bbbb','c','ddd','ee']
sortStringList.sort({it ->
return it.size()
})
println sortStringList
Groovy 映射 (Maps)
映射 (Map,也称为关联数组,字典,表和散列),是对象引用的无序集合。Map 集合中的元素由键值访问。Map 中使用的键可以是任意类。当我们插入到 Map 集合中时,需要两个值:键和值。
以下是一些映射的例子:
- ['TopicName':'Lists','TopicName':'Maps'] - 具有 TopicName 作为键的键值对的集合及其相应的值。
- [:] - 空映射。
Map 中的方法
- boolean containsKey(Object key) 此映射是否包含此 key?
- boolean containsKey(Object value) 此映射是否包含 value?
- Object get(Object key) 查找此 Map 中的键并返回相应的值。如果此映射中没有键的条目,则返回 null。
- Set keySet() 获取此映射中的一组 key。
- Object put(Object key, Object value) 将指定的值与此映射中的指定键相关联。 如果此映射先前包含此键的映射,则旧值将替换为指定的值。返回值,返回之前的 key 位置的 value,如果第一次插入那么 value 为 null
- int size() 返回此映射中的键值映射的数量。
- Collection values() 返回此映射中包含的 value 的集合视图。(返回所有的 value 值)
案例 1:
private static void testMap() {
def map = ["topicName1": "list", 'topicName': 'map', null: "nullValue"]
println(map) // [topicName1:list, topicName:map, null:nullValue]
println(map.containsKey("topicName")) // true
println(map.containsKey("null")) // true
println(map.containsValue("map")) // true
println(map.put("key1", "value2")) // null
println(map.put("key1", "value1")) // value2
println(map) // [topicName1:list, topicName:map, null:nullValue, key1:value1]
println(map.size()) // 4
println(map.values()) // [list, map, nullValue, value1]
}
private static void testMap() {
def map = ["topicName1": "list", 'topicName': 'map', null: "nullValue"]
println(map) // [topicName1:list, topicName:map, null:nullValue]
println(map.containsKey("topicName")) // true
println(map.containsKey("null")) // true
println(map.containsValue("map")) // true
println(map.put("key1", "value2")) // null
println(map.put("key1", "value1")) // value2
println(map) // [topicName1:list, topicName:map, null:nullValue, key1:value1]
println(map.size()) // 4
println(map.values()) // [list, map, nullValue, value1]
}
案例 2:
////定义与读取
//def colors=[red:'ff0000',green:'00ff00',blue:'0000ff']
//println colors['red']
//println colors.red
////如果使用colors.class 会把class当成一个键
////class java.util.LinkedHashMap
//println colors.getClass()
//
////添加普通对象
//colors.yellow='ffff00'
//println colors
////添加集合对象
//colors.map = [key1:1,key2:2]
//println colors.toMapString()
//遍历map
def teachers = [
1: [number: '001', name: 'jett'],
4: [number: '004', name: 'alven'],
3: [number: '003', name: 'lance'],
2: [number: '002', name: 'leo'],
6: [number: '006', name: 'allen'],
5: [number: '005', name: 'zero'],
7: [number: '007', name: 'derry'],
8: [number: '008', name: 'jett']
]
//用键值对的方式
//teachers.each {def key,def value ->
// println "key=${key}---value=${value}"
//}
//用entry对象的方式
//teachers.each {def teacher ->
// println "key=${teacher.key}---value=${teacher.value}"
//}
//带索引的方式
//teachers.eachWithIndex{ def teacher,int index->
// println "index=${index}---key=${teacher.key}---value=${teacher.value}"
//}
//teachers.eachWithIndex{ def key,def value,int index->
// println "index=${index}---key=${key}---value=${value}"
//}
//map的查找
//def entry=teachers.find{def teacher ->
// return teacher.value.name=='jett'
//}
//println entry
//def entry=teachers.findAll{def teacher ->
// return teacher.value.name=='jett'
//}
//println entry
//def count=teachers.count{def teacher ->
// return teacher.value.name=='jett'
//}
//println count
//实现嵌套查询
def number=teachers.findAll{def teacher->
return teacher.value.name=='jett'
}.collect(){
return it.value.number
}
println number.toListString()
//实现分组查询
def group=teachers.groupBy {def teacher ->
return teacher.value.name=='jett' ? 'group1' : 'group2'
}
println group.toMapString()
//排序 注意:map会返回一个新的map list是在原来的list中进行排序
def sort=teachers.sort{def t1,def t2 ->
return t1.key > t2.key ? 1 : -1
}
println sort.toMapString()
Groovy 日期和时间
类 Date 表示特定的时刻,具有毫秒精度。 Date 类有两个构造函数
- Date() 当前日期和时间
- Date (长毫秒) 分配一个 Date 对象并将其初始化以表示自标准基准时间(称为 " 该历元 ",即 1970 年 1 月 1 日,00:00:00 GMT)起指定的毫秒数。
Date 中方法
- after() 测试此日期是否在指定日期之后。
- before() 测试此日期是否在指定日期之前。
- equals() 比较两个日期的相等性。当且仅当参数不为 null 时,结果为 true,并且是表示与该对象时间相同的时间点(毫秒)的 Date 对象。
- compareTo() 比较两个日期的顺序。
- getTime() 返回自此 Date 对象表示的 1970 年 1 月 1 日,00:00:00 GMT 以来的毫秒数。
- setTime() 设置此 Date 对象以表示一个时间点,即 1970 年 1 月 1 日 00:00:00 GMT 之后的时间毫秒。
- toString() 将此 Date 对象转换为字符串
Groovy 正则表达式
正则表达式用于在文本中查找子字符串的模式。Groovy 使用 ~regex 表达式本地支持正则表达式。regex 包含的文本表示用于比较的表达式。
当 Groovy 运算符 =~ 在 if 或 while 语句中作为判断表达式时,左侧的 String 操作数与右侧的正则表达式操作数匹配,判断条件为 true
if ("hacketfafdf" =~ "hacket") {
println("true") // true
}
正则表达式规则,可以使用的特殊字符
- 有两个特殊的位置字符用于表示一行的开始和结束:
^和$ - 正则表达式也可以包括量词:
+表示一次或多次,应用于表达式的前一个元素;*表示零个或多个出现;?表示零或一次 - 元字符
{和}用于匹配前一个字符的特定数量的实例 - 在正则表达式中,
.可以表示任何字符。(通配符) - 正则表达式可以包括字符类。一组字符可以作为简单的字符序列,包含在元字符
[和]中,如:[aeiou]。对于字母或数字范围,可以使用[a-z]或[a-mA-M]中的-分隔符。字符类的补码由方括号内的前导插入符号表示,如[^a-z],并表示除指定的字符以外的所有字符。
Groovy 异常处理
同 Java 异常处理机制
Groovy 面向对象
同 Java 面向对象
class/接口
抽象
封装
继承
多态
trait
接口
- 接口中不能定义非 public 方法的
/**
* 接口中不能定义非public方法的
*/
interface Action {
void eat()
void drink()
void play()
}
类
package objectorention
// 1.在groovy中所有的类型 默认都是public
// 2.所有的类都是继承自GroovyObject
class Person implements DefaultAction{
String name
Integer age
def increateseAge(Integer year){
this.age+=year
}
@Override
void eat() {
}
}
创建对象和使用对象:
package objectorention
def person=new Person(name:'jett',age:18)
println "name="+person.name+" age="+person.age
println "name="+person.getName()+" age="+person.getAge()
person.increateseAge(10)
println "name="+person.name+" age="+person.age
person.play()
trait
package objectorention
trait DefaultAction {
abstract void eat()
void play(){
println 'I can play!'
}
}
Groovy 泛型
同 Java 泛型
Groovy 之 XML
XML Markup Builder
MarkupBuilder 用于构造整个 XML 文档。通过首先创建 XML 文档类的对象来创建 XML 文档。一旦创建了对象,可以调用伪方法来创建 XML 文档的各种元素。
def markBuilder = new MarkupBuilder()
markBuilder.collection(shelf: 'New Arrivals') {
movie(title: 'Enemy Behind')
type('War, Thriller')
format('DVD')
year('2003')
rating('PG')
stars(10)
description('Talk about a US-Japan war')
}
结果:
<collection shelf='New Arrivals'>
<movie title='Enemy Behind' />
<type>War, Thriller</type>
<format>DVD</format>
<year>2003</year>
<rating>PG</rating>
<stars>10</stars>
<description>Talk about a US-Japan war</description>
</collection>
- markBuilder.collection() 这是一个标记生成器,用于创建
<collection></collection>的头 XML 标签 - movie(title : 'Enemy Behind') - 这些伪方法使用此方法创建带有值的标记的子标记。通过指定一个名为 title 的值,这实际上表示需要为该元素创建一个属性。
- 向伪方法提供闭包以创建 XML 文档的剩余元素。
- 初始化类 MarkupBuilder 的默认构造函数,以便将生成的 XML 发布到标准输出流
XmlParser
XmlParser 来解析 XML 文档
案例:
import groovy.xml.MarkupBuilder
import groovy.xml.XmlSlurper
final String xml='''
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.jvm_demo_20200601">
<test>12345</test>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".MainActivity2">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
'''
////解析XML数据
//def xmlSluper=new XmlSlurper()
//def result=xmlSluper.parseText(xml)
//println result.@package
//println result.test.text()
////读取有域名空间的节点
//result.declareNamespace('android':'http://schemas.android.com/apk/res/android')
//println result.application.@'android:allowBackup'
//println result.application.activity[0].@'android:name'
//println result.application.activity[1].@'android:name'
//
////遍历XML节点
//result.application.activity.each{activity ->
// println activity.@'android:name'
//}
/**
* 生成XML格式数据
* <html>
* <title id='123',name='android'>xml生成
* <person></person>
* </title>
* <body name='java'>
* <activity id='001' class='MainActivity'>abc</activity>
* <activity id='002' class='SecActivity'>abc</activity>
* </body>
* </html>
*/
def sw=new StringWriter()
def xmlBuilder=new MarkupBuilder(sw)
xmlBuilder.html(){
title(id:'123',name:'android','xml生成'){
person()
}
body(name:'java'){
activity(id:'001',class:'MainActivity','abc')
activity(id:'002',class:'SecActivity','abc')
}
}
println sw
Groovy 之 JSON
用 Groovy 来解析和生成 JSON 对象
| API | 功能 |
|---|---|
| JsonSlurper | JsonSlurper 是一个将 JSON 文本或阅读器内容(reader content)解析为 Groovy 中的数据结构(如 Map、List、Integer、Double、Boolean 和 String) |
| JsonOutput | 代表了将 Groovy 对象序列化成 JSON 字符串 |
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
// 对象转成json字符串
def list=[new Person(name:'jett',age:18),
new Person(name:'lance',age:18)]
println JsonOutput.toJson(list)
// 格式化
def json=JsonOutput.toJson(list)
println JsonOutput.prettyPrint(json)
// json字符串转成对象
def jsonSluper=new JsonSlurper()
def object=jsonSluper.parse("[{\"age\":18,\"name\":\"jett\"},{\"age\":18,\"name\":\"lance\"}]".getBytes())
println object
def object2=jsonSluper.parse("[{\"abc\":\"jett\"}]".getBytes())
println object2.abc
class Person {
String name
Integer age
}
生成 json
static def json(List<String> atUsers) {
def json = new JsonBuilder()
json {
msgtype "markdown"
markdown {
title "title"
text "text"
}
at {
if (!atUsers.isEmpty()) {
atMobiles(atUsers)
}
isAtAll false
}
}
return json.toString()
}
输出:
{"msgtype":"markdown","markdown":{"title":"title","text":"text"},"at":{"atMobiles":["134","135"],"isAtAll":false}}
Groovy 注解 (Annotations)
用@interface 表示,类似 Java。
@interface Simple { // 不能嵌套在class内部
int status() // default 0
}
@Simple(status = 1)
static class User {
String username
int age
}
元注解(Meta Annotations)
Groovy 闭包 (Closure)
见下面的 Groovy语法之闭包.md
Groovy 对 Java 的优化
- 表达式后面的分号是可选的
- 每个类、构造器和方法默认是 public 的
- groovy 方法体中的最后一个表达式的值会被作为返回值
- groovy 编译器自动加上 getter/setter 方法
- 类的属性可以通过点 (.) 来获取,底层是 groovy 自动调用 getter/setter 方法
- 用 == 比较两个类的实例,底层调用的是 equals() 方法,这个操作也避免了 npe 异常
可选的括号
Groovy 中如果方法签名需要至少一个参数的话,则方法调用可以省略括号。
initProjectVersion(1,2)
initProjectVersion 1,2
字符串
// 1、单引号
def myStr1 = `This is a single-quoted String`
// 2、双引号,也叫GString。可${str}来表示
def myStr2 = "This is a double-quoted String"
// 3、三引号
def myStr3 = """
This is
a multiline String
"""
命名参数
一个类没有默认构造器,Groovy 会调用类的默认构造器,然后为每个参数调用对应的 setter 方法
ProjectVersion pv = new ProjectVersion(major:1,minor:10)
闭包
- 闭包参数
隐式闭包参数 it
显示闭包参数 - 闭包返回值
- 闭包作为方法参数 Closure
- 闭包委托
闭包代码在委托的闭包上执行。默认的,这个委托就是闭包的所有者(你在 Groovy 脚本中定义了一个闭包,那么这个闭包的所有者就是 groogy.lang.Script 实例)。闭包的隐式变量 delegate 允许你重新定义默认的所有者。
Groovy 新的特性
trait
trait 是语言的结构构造,它允许:
- 行为的组成
- 接口的运行时实现
- 和静态类型检查/编译相兼容
它们可以被看作是承载默认实现和状态的接口,用 trait 关键字定义。
trait Marks {
void displayMarks() {
println("display Marks")
}
}
然后可以用 implement 关键字类似接口的方式实现 trait
trait Marks {
void displayMarks() {
println("display Marks")
}
}
static class StuMarks implements Marks {
int studentID
int marks1;
}
trait 可以实现接口
trait 可以实现接口,在这种情况下,使用 implements 关键字来实现声明的接口。
interface Total {
void displayTotal()
}
trait Marks implements Total {
void displayMarks() {
println("display Marks")
}
@Override
void displayTotal() {
println("display total")
}
}
class StuMarks implements Marks {
int studentID
int marks1
}
trait 可以定义属性
trait 可以定义属性:
trait Marks {
int Marks1
void displayMarks() {
this.Marks1 = 10
println("display Marks:" + Marks1)
}
}
trait 行为组合(Composition of Behaviors)
类似接口一样,可以被同一个 class 实现多个 trait,然后该 class 就具备了这两个 trait 的行为。
trait Marks {
void DisplayMarks() {
println("Marks1");
}
}
trait Total {
void DisplayTotal() {
println("Total");
}
}
class Student implements Marks,Total {
int StudentID
}
继承 trait(Extending Traits)
trait 可以继承另外一个 trait,使用 extends 关键字。
class Example {
static void main(String[] args) {
Student st = new Student();
st.StudentID = 1;
println(st.DisplayMarks());
}
}
trait Marks {
void DisplayMarks() {
println("Marks1");
}
}
trait Total extends Marks {
void DisplayMarks() {
println("Total");
}
}
class Student implements Total {
int StudentID
}
Groovy 闭包
闭包的定义
闭包是 Groovy 中非常重要的一个数据类型或者说一种概念。闭包是一种数据类型,它代表了一段可执行的代码。闭包的语法定义:
{ [closureParameters -> ] statements }
- 其中
[closureParameters->]部分是一个可选的以逗号分隔的参数列表 - statements 可以为空,一行或者多行代码
当参数列表确定时,
->是必须的,他负责把参数和闭包的代码块分开,代码块可以由一行或者多行语句组成
一些闭包常用的定义形式:
{ item++ }
{ -> item++ }
{ println it }
{ it -> println it }
{ name -> println name }
{ String x, int y ->
println "hey ${x} the value is ${y}"
}
{ reader ->
def line = reader.readLine()
line.trim()
}
如果闭包没有定义参数的话,则隐含有一个参数,这个参数的名字交 it,和 this 作用类似,it 代表闭包的参数。
闭包作为一个对象
闭包实质上是一个 groovy.lang.Closure 类的实例,虽然他是一个代码块,但是他可以为任何一个变量或者字段赋值,闭包中可以包含代码的逻辑,闭包中的最后一行语句,表示该闭包的返回值,不论该语句是否有 return 关键字
// 可以将闭包分配给一个变量,这个变量就是groovy.lang.Closure的一个实例
def listener = { e -> println "Clicked on $e.source" }
assert listener instanceof Closure
// 如果不使用def,可以将一个闭包分配给一个类型为groovy.lang.Closure的变量
Closure callback = { println 'Done!' }
// 可以通过使用groovy.lang.Closure的泛型来指定闭包的返回类型
Closure<Boolean> isTextFile = {
File it -> it.name.endsWith('.txt')
}
调用闭包
下面是一段简短的 closure:
void test1() {
def clos = { println "i am a closure!" }
clos.call()
// clos() // 这个也可以调用
}
上面 { println "i am a closure!"} 称之为闭包。这段代码块(闭包)可以通过 闭包.call() 或 闭包() 或来执行。
- 闭包作为一个匿名的代码块,可以像方法那样被调用
- 闭包内的代码只有在闭包被调用时才会执行,调用可以像常规方法那样来完成
- 可以显式的调用 call 方法调用
- 和方法不一样的是,闭包始终有一个返回值
闭包的参数 (Formal parameters in closures)
闭包的参数遵循与常规方法的参数相同的原则 (参数用 , 分隔):
- 可选的类型
- 名字
- 可选的默认值
闭包的显式参数
闭包也可以包含形式参数,以使它们更有用,就像 Groovy 中的方法一样:
void test1() {
def clos = { param -> println "i am a closure!${param}" }
clos.call("hacket")
}
上面的代码用 $param 或 ${param} 是 closure 接收一个参数,当通过 call 调用闭包时,可以传递一个参数给闭包。
上面的代码也等价于:
void test1() {
def clos = { println "i am a closure!$it" }
clos.call("hacket")
}
示例:
// 只有1个参数,只有参数名,没有类型
def closureWithOneArg = { str -> str.toUpperCase() }
assert closureWithOneArg('groovy') == 'GROOVY'
// 只有1个参数,有参数类型和参数名字
def closureWithOneArgAndExplicitType = { String str -> str.toUpperCase() }
assert closureWithOneArgAndExplicitType('groovy') == 'GROOVY'
// 有2个参数,只有参数名,没有类型
def closureWithTwoArgs = { a,b -> a+b }
assert closureWithTwoArgs(1,2) == 3
// 有2个参数,有参数名和参数类型
def closureWithTwoArgsAndExplicitTypes = { int a, int b -> a+b }
assert closureWithTwoArgsAndExplicitTypes(1,2) == 3
// 有2个参数,第1个参数只有名字,第2个参数有参数类型和名字
def closureWithTwoArgsAndOptionalTypes = { a, int b -> a+b }
assert closureWithTwoArgsAndOptionalTypes(1,2) == 3
// 有2个参数,第1个参数有参数类型和参数名字,第2个参数有参数类型、参数名字和参数默认值
def closureWithTwoArgAndDefaultValue = { int a, int b=2 -> a+b }
assert closureWithTwoArgAndDefaultValue(1) == 3
闭包的隐式参数
当一个闭包没有明确定义一个参数列表(使用 ->)时,闭包总是定义一个隐式参数,并将其命名为 it。
def greeting = { "Hello, $it!" }
assert greeting('Patrick') == 'Hello, Patrick!'
// 等同于
def greeting = { it -> "Hello, $it!" }
assert greeting('Patrick') == 'Hello, Patrick!'
如果想声明一个不接受参数的闭包,并且必须限制为没有参数的调用,那么必须声明一个明确的空参数列表:
def magicNumber = { -> 42 }
// this call will fail because the closure doesn't accept any argument
magicNumber(11)
- 当闭包作为闭包或方法的最后一个参数,可以将闭包从参数圆括号中提取出来接在最后,如果闭包是唯一的一个参数,则闭包或方法参数所在的圆括号也可以省略
- 对于有多个闭包参数的,只要是在参数声明最后的,均可以按上述方式省略
def runTwice = { a, c -> c(c(a)) }
assert runTwice( 5, {it * 3} ) == 45 //usual syntax
assert runTwice( 5 ){it * 3} == 45
//when closure is last param, can put it after the param list
def runTwiceAndConcat = { c -> c() + c() }
assert runTwiceAndConcat( { 'plate' } ) == 'plateplate' //usual syntax
assert runTwiceAndConcat(){ 'bowl' } == 'bowlbowl' //shortcut form
assert runTwiceAndConcat{ 'mug' } == 'mugmug'
//can skip parens altogether if closure is only param
def runTwoClosures = { a, c1, c2 -> c1(c2(a)) }
//when more than one closure as last params
assert runTwoClosures( 5, {it*3}, {it*4} ) == 60 //usual syntax
assert runTwoClosures( 5 ){it*3}{it*4} == 60 //shortcut form
默认参数
// 闭包支持默认参数
cClosure("hello","clat") // hello, clat!
def dClosure = {
name,address = "shenzhen" ->
println "${name},${address}!"
}
dClosure("hacket"); // hacket,shenzhen!
闭包和变量 (Closures and Variables)
闭包代码块中可以引用外部定义的变量:
def str1 = "Hello"
def clos = { param -> println "${str1} $param" }
clos.call(" World!") // Hello World!
str1 = "Welcome"
clos(" World!") // Welcome World!
带返回值的闭包
//定义与使用
//无参数的闭包
//def closure={
// println "hello groovy!"
//}
//closure()
//closure.call()
//带参数的闭包
//def closure={String name,int age->
// println "hello ${name}:age ${age}"
//}
//closure.call("jett",18)
//带默认参数
//def closure={
// println "hello ${it}"
//}
//closure.call("jett")
//闭包的返回值
def closure={
println "hello ${it}"
return "123"
}
def result=closure.call("jett")
println "result="+result
可变参数
闭包可以象任何其他方法一样声明可变参数
def concat1 = { String... args -> args.join('') }
assert concat1('abc','def') == 'abcdef'
def concat2 = { String[] args -> args.join('') }
assert concat2('abc', 'def') == 'abcdef'
def multiConcat = { int n, String... args ->
args.join('')*n
}
assert multiConcat(2, 'abc','def') == 'abcdefabcdef'
应用闭包
在方法中使用闭包
- 闭包也可以作为方法的参数。
在 Groovy 中,很多用于数据类型(如 List 和 Map)的内置方法都有闭包作为参数类型。
static void test3() {
def str1 = "Hello"
def clos = { param -> println "$str1 $param" }
display(clos)
}
def static display(clo) {
clo.call("Inner")
}
- 匿名内联函数,也称为一个闭包
/**
* 匿名内联函数,也称为一个闭包。
* 基本类型相关的API
*/
int x=fab(5)
int fab(int number){
int result=1;
1.upto(number,{num -> result *= num})
return result
}
println x;
int fab2(int number){
int result=1
number.downto(1){
num-> result*=num
}
return result
}
println fab2(5)
int sum(int number){
int result=0;
number.times {
num-> result+=num;
}
return result
}
println sum(5)
和 String 相关的 API
/**
* 和String相关的API
*/
String str="2 and 3 is 5"
//each遍历
//str.each {
// String s->print s.multiply(2)
//}
//find查找符合条件的第一个字符
//println str.find{
// String s->s.isNumber()
//}
//findAll 查找符合条件的所有字符
//def list=str.findAll{
// String s-> s.isNumber()
//}
//println list.toListString()
//any 查找是否存在符合条件的字符
//def result=str.any{
// String s->s.isNumber()
//}
//println result
//every查找是否所有字符都符合条件
//def result=str.every{
// String s->s.isNumber()
//}
//println result
//对str的每一位单独操作后的结果保存到一个集合中
def list=str.collect{
it.toUpperCase()
}
println list.toListString()
List/Map 中的闭包
static void test4() {
def list = [11, 23, 12, 14]
list.each { println it }
list.each { num ->
if (num % 2 == 0) {
println("偶数:" + num)
}
}
def map = ["TopicName": "Maps", "TopicDescription": "Methods in Maps"]
map.each { println it }
map.each { println "${it.key} maps to $it.value" }
}
- find()
find 方法查找集合中与某个条件匹配的第一个值 - findAll()
它找到接收对象中与闭合条件匹配的所有值 - any()
集合中至少有一个元素满足条件 - every()
集合中所有的元素满足条件 - collection()
该方法通过集合收集迭代,使用闭包作为变换器将每个元素转换为新值。(通过 closure 表达式变换为一个新的集合)
def list2 = [1, 2, 0, 43, 34]
def value = list2.find({ num -> num >= 2 })
println(value) // 2
def val2 = list2.findAll { num -> num >= 2 }
val2.each { println it } // 2 43 34
def any = list2.any { it > 2 }
println(any) // true
def every = list2.every { it > 2 }
println(every) // false
def list3 = list2.collect { it * it }
list3.each { println(it) } // 1 4 0 1849 1156
Groovy Bean 和闭包
// groovy bean
class GroovyBeanExample {
private String name
}
def bean = new GroovyBeanExample();
bean.name = "my name is hacket!"
println bean.name
// 闭包
// 闭包是一个可执行的代码块。
// 1、闭包
def aClosuer = {
println "Hello Closure!";
}
println aClosuer; // my$_run_closure1@962257c
// 闭包的调用
aClosuer.call(); // 使用call()调用闭包 Hello Closure!
aClosuer(); // 调用闭包的简写方式,类似于方法调用。 Hello Closure!
// 闭包在调用的时候才会执行
// 2、隐式参数化闭包
def bClosure = {
println "Hello ${it}" //it 是闭包的单个隐含参数。
}
bClosure.call("clat") // Hello clat
bClosure("clat") // Hello clat
bClosure "clat" // Hello clat
// 3、显示参数
def cClosure = {
name,address ->
println "${name},${address}!"
}
// 闭包支持默认参数
cClosure("hello","clat") // hello, clat!
def dClosure = {
name,address = "shenzhen" ->
println "${name},${address}!"
}
dClosure("hacket"); // hacket,shenzhen!
println('=============================');
// 4、闭包作用域
def name = "hacket..."
def eClosure = {
println name;
}
eClosure(); // hacket...
def aMethod(Closure closure){
name = "a hacket"; // 这里的name只是方法的局部变量,闭包不能访问
closure();
}
aMethod(eClosure); // hacket...
def fClosure = {
name = "f hacket"; // 这里的name是闭包里面的,实就是可执行片段修改了外围的一个变量
eClosure();
}
fClosure(); // f hacket
// 5、闭包返回值
def gClosure = {
number ->
return number * 2;
}
println gClosure(3); // 6
// 6、闭包与集合、字符串
// 遍历集合
def nameList = ["clat","escaflone","Aldern"]
nameList.each{
myname ->
print myname+"_"
}
println ""
// clat_escaflone_Aldern_
// 遍历map
def nameMap = [1:'clat',2:'escaflone',3:'Aldern']
nameMap.each{
map ->
println map.key +":"+ map.value
}
// 1:clat
// 2:escaflone
// 3:Aldern
// 遍历Range
(1..<10).each{
print it+"-"
}
// 1-2-3-4-5-6-7-8-9-
println ""
// 遍历String
"hacket".each{
print it+"~"
}
// h~a~c~k~e~t~
println ""
// 7、闭包嵌套
def outClosure = {
country ->
println "country-->"+country;
def innerClosure = {
city ->
println "city-->"+city
}
innerClosure("shenzhen");
}
outClosure("China");
// country-->China
// city-->shenzhen
闭包委托策略(this,owner,deleate)
Groovy 的闭包比 Java 的 Lambda 表达式功能更强大。原因就是 Groovy 闭包可以修改委托对象和委托策略。这样 Groovy 就可以实现非常优美的领域描述语言(DSL)了。Gradle 就是一个鲜明的例子。
Groovy 闭包有三个相关的对象:
- this 即闭包定义所在的类
- owner 即闭包定义所在的对象或闭包
- delegate 即闭包中引用的第三方对象
this
this 代表闭包定义所在的类;在闭包中,调用 getThisObject() 将返回闭包定义的闭包类,和显式的调用 this 一样
class Enclosing {
def run() {
def whatIsThisObject = {
println("Closure.this=${this}") // this=me.hacket.groovy.Enclosing@1603cd68
getThisObject()
}
assert whatIsThisObject() == this // true
println("Enclosing.this=${this}") // this=me.hacket.groovy.Enclosing@1603cd68
def whatIsThis = { this }
assert whatIsThis() == this // true
}
}
class EnclosedInInnerClass {
class Inner {
Closure cl = { this } // 这个this为Inner对象,返回的就是inner对象
}
void run() {
def inner = new Inner()
assert inner.cl.call() == inner // true
}
}
owner
owner 代表闭包定义所在的对象或闭包
delegate
可以使用 delegate 属性或者调用 getDelegate 方法来访问闭包中的 delegate,delegate 在 Groovy 中是一个很重要的概念,delegate 需要我们手动指定;默认 delegate 和 owner 一样
class Enclosing {
void run() {
def cl = { getDelegate() }
def cl2 = { delegate }
assert cl() == cl2()
assert cl() == this
def enclosed = {
{ -> delegate }.call()
}
assert enclosed() == enclosed
}
}
闭包的 delegate 可以被改变成任何对象。 我们通过创建两个不是彼此的子类的类来说明这一点,但都定义了一个名为 name 的属性:
class Person {
String name
}
class Thing {
String name
}
def p = new Person(name: 'Norman')
def t = new Thing(name: 'Teapot')
// 然后定义一个闭包通过delegate来调用name属性
def upperCasedName = { delegate.name.toUpperCase() }
// 然后通过更改闭包的委托,可以看到目标对象将会改变
upperCasedName.delegate = p
assert upperCasedName() == 'NORMAN'
upperCasedName.delegate = t
assert upperCasedName() == 'TEAPOT'
Delegation strategy(委托策略)
无论什么时候,在闭包中,访问某个属性时都没有明确地设置接收者对象,那么就会调用一个委托策略
class Person2 {
String name
}
static void main(String[] args) {
def p = new Person2(name: 'Igor')
def cl = { name.toUpperCase() }
cl.delegate = p
// println("cl.owner=${cl.owner}, name=${cl.owner.name}") // MissingPropertyException
println("cl.delegate=${cl.delegate}, name=${cl.delegate.name}") // cl.delegate=me.hacket.groovy.Person2@6ce86ce1, name=Igor
// name property be resolved transparently on the delegate object!
assert cl() == 'IGOR' // true
}
调用 name 属性并没有执行是谁的 name 属性,然后把闭包 cl 的 delegate 设置为 p,这里没有显式的给 delegate 设置一个接收者,但是能成功访问到 name 属性,因为相应的属性解析策略:
- Closure.OWNER_FIRST,默认策略,首先从 owner 上寻找属性或方法,找不到则在 delegate 上寻找。
- Closure.DELEGATE_FIRST,和上面相反。首先从 delegate 上寻找属性或者方法
- Closure.OWNER_ONLY,只在 owner 上寻找,delegate 被忽略。
- Closure.DELEGATE_ONLY,和上面相反。只在 delegate 上寻找,owner 被忽略。
- Closure.TO_SELF,高级选项,让开发者自定义策略。
示例 1(Closure.OWNER_FIRST):
class Person3 {
String name
def pretty = { "My name is $name" }
String toString() {
pretty.call()
}
}
class Thing {
String name
}
static void main(String[] args) {
def p = new Person3(name: 'Sarah')
def t = new Thing(name: 'Teapot')
assert p.toString() == 'My name is Sarah'
p.pretty.delegate = t // 默认Closure.OWNER_FIRST,从owner寻找
assert p.toString() == 'My name is Sarah' // true
}
Person 和 Thing 都定义了 name 属性,使用默认策略,首先会在 Owner 上寻找,所以即使我们把 delegate 换成 t,输出结果还是一样的
示例 2(Closure.DELEGATE_FIRST):
p.pretty.resolveStrategy = Closure.DELEGATE_FIRST
assert p.toString() == 'My name is Teapot'
把委托策略改成 Closure.DELEGATE_FIRST,则会首先去 delegate 寻找 name 属性,如果没有找到,再去 owner 上寻找,但是 delegate 有 name 的定义,所以输出结果为 "My name is Teapot"
示例 3(delegate first vs delegate only):
class Person {
String name
int age
def fetchAge = { age }
}
class Thing {
String name
}
def p = new Person(name:'Jessica', age:42)
def t = new Thing(name:'Printer')
def cl = p.fetchAge
cl.delegate = p
assert cl() == 42
cl.delegate = t
assert cl() == 42
cl.resolveStrategy = Closure.DELEGATE_ONLY
cl.delegate = p
assert cl() == 42
cl.delegate = t
try {
cl()
assert false
} catch (MissingPropertyException ex) {
// "age" is not defined on the delegate
}
only 时,在对应的对象上找不到时,不会接着去别处找,而是抛出一个异常
测试
在普通类或方法中定义闭包,三者是相同的
class Person {
def static classClouser = {
println "classClouser:" + this.hashCode()
println "classClouser:" + owner.hashCode()
println "classClouser:" + delegate.hashCode()
}
def static method() {
def classClouser = {
println "methodclassClouser:" + this.hashCode()
println "methodclassClouser:" + owner.hashCode()
println "methodclassClouser:" + delegate.hashCode()
}
classClouser.call()
}
}
public static void main(String[] args) {
Person.classClouser.call()
Person.method()
}
输出:
classClouser:1321203216
classClouser:1321203216
classClouser:1321203216
methodclassClouser:1321203216
methodclassClouser:1321203216
methodclassClouser:1321203216
/**
* 闭包的三个重要变量:this,owner,deleate
*/
//在同一个闭包中,都是相同的对象
//def scriptClouser = {
// println this//代表闭包定义处的类
// println owner//代表闭包定义处的类或者对象
// println delegate//代表任意对象,delegate默认为owner指向的对象
//}
//scriptClouser.call()
//在普通类或方法中定义闭包,三者是相同的
class Person {
// def static classClouser = {
// println "classClouser:" + this
// println "classClouser:" + owner
// println "classClouser:" + delegate
// }
//
// def static method() {
// def classClouser = {
// println "methodclassClouser:" + this
// println "methodclassClouser:" + owner
// println "methodclassClouser:" + delegate
// }
// classClouser.call()
// }
}
//Person.classClouser.call()
//Person.method()
//闭包内定义闭包 this内部对象 owner和delegate是外部对象
//def nestClouser = {
// def innerClouser = {
// println "innerClouser:" + this
// println "innerClouser:" + owner
// println "innerClouser:" + delegate
// }
// innerClouser.call()
//}
//nestClouser.call()
//修改默认的delegate对象
//Person p=new Person();
//def nestClouser = {
// def innerClouser = {
// println "innerClouser:" + this
// println "innerClouser:" + owner
// println "innerClouser:" + delegate
// }
// innerClouser.delegate=p;
// innerClouser.call()
//}
//nestClouser.call()
/**
* 闭包的委托策略
*/
class Student{
String name
def pretty={"My name is ${name}"}
String toString(){
pretty.call()
}
}
def student=new Student(name:'jett')
class Teacher{
String name
}
def teacher=new Teacher(name:'andy')
student.pretty.delegate=teacher
//闭包的委托策略
student.pretty.resolveStrategy=Closure.DELEGATE_FIRST
println student.toString()
Ref
Groovy 之 MOP(invokeMethod 和 methodMissing 方法)
invokeMethod 和 methodMissing
invokeMethod方法可以分派所有的方法,包括一个类已经实现了的和未实现的方法;而它实现上面的功能是通过这个类实现GroovyInterceptable接口达到的,未实现GroovyInterceptable接口时,invokeMethod 和 methodMission 功能一致methodMissing方法则只能分派一个类未实现的方法,无论它是否实现了GroovyInterceptable接口。
如果我们想让一个方法来管理一个类所有方法的调用,那么我们必须使用 "invokeMethod" 方法;如果我们只想通过一个方法来管理一个类的所有 "missing method",即不能被分派出去的方法,那么使用 "methodMissing" 方法是比较有效的;当然,"invokeMethod" 方法也能实现 "methodMissing" 方法的功能。
测试
- invokeMethod 未实现 GroovyInterceptable
class InvokeTestor1 {
def hello() {
'invoke hello directly'
}
String invokeMethod(String name,Object args) {
return "invokeMethod unknown method $name(${args.join(',')})"
}
static main(args) {
def it = new InvokeTestor1()
println it.hello()
println it.foo("mark", 19)
}
}
输出:
invoke hello directly
invokeMethod unknown method foo(mark,19)
- invokeMethod 实现 GroovyInterceptable
class InvokeTestor1 implements GroovyInterceptable {
def hello() {
'invoke hello directly'
}
String invokeMethod(String name, Object args) {
return "invokeMethod unknown method $name(${args.join(',')})"
}
static main(args) {
def it = new InvokeTestor1()
println it.hello()
println it.foo("mark", 19)
}
}
输出:
invokeMethod unknown method hello()
invokeMethod unknown method foo(mark,19)
- invokeMethod 实现 GroovyInterceptable 和为实现 GroovyInterceptable
class InvokeTestor1 implements GroovyInterceptable {
def hello() {
'invoke hello directly'
}
def methodMissing(String name, args) {
return "methodMissing unknown method $name(${args.join(',')})"
}
static main(args) {
def it = new InvokeTestor1()
println it.hello()
println it.foo("mark", 19, 20, 30)
}
}
输出:
invoke hello directly
methodMissing unknown method foo(mark,19,20,30)